package tpl 

import (
	"regexp"
	"bufio"
	"os"
	"strings"
    "fmt"
    "bytes"
    "template"
)

type Template struct{
    BaseDir string
}

type TplError string

func (e TplError) String() string { return (string)(e) }

func (tpl *Template) Render(data interface{}, path string) string {
	d, err := parseTpl(path)
	if err != nil {
		panic(fmt.Sprintf("Parse template %s failed : %s", path, err))
	}

	var result string
	buffer := bytes.NewBufferString(result)

	baseFilePath, hasBase:= d["base-template"]
    d["base-template"] = "", false

	if !hasBase {
		tpl, err := template.ParseFile(path, nil)
		if err != nil {
			panic(fmt.Sprintf("Parse template %s falied : %s", path, err))
		}

		tpl.Execute(buffer, data)
		result = buffer.String()

	} else {
		baseFilePath = tpl.BaseDir + "master" + `/` + strings.Trim(strings.Trim(strings.Trim(baseFilePath, " "), "\n"), "\r")

		for k, v := range d {
			t, _ := template.Parse(v, nil)
			var temp string
			buf := bytes.NewBufferString(temp)
			t.Execute(buf, data)
			d[k] = buf.String()
		}

		tpl, err := template.ParseFile(baseFilePath, nil)
		if err != nil {
			panic(fmt.Sprintf("Parse template %s falied : %s", baseFilePath, err))
		}

		tpl.Execute(buffer, d)
		result = buffer.String()

	}

	return result
}

func parseTpl(path string) (map[string]string, os.Error) {
	s, err := os.Open(path, os.O_RDONLY, 0)
	if err != nil {
		return nil, (TplError)("Cannot read file: " + err.String())
	}

	bufReader := bufio.NewReader(s)

	reBlockStart, _ := regexp.Compile(` *{ *.block +([a-zA-Z]+) *} *`)
	reBlockEnd, _ := regexp.Compile(` *{ *.endblock *} *`)
	reExtend, _ := regexp.Compile(` *{ *.extends +"([a-zA-Z\./]+)" *} *`)

	var base, block, value string
	d := make(map[string]string)

	for {
		l, err := bufReader.ReadString('\n')

		if err != nil {
			if err != os.EOF {
				return nil, err
			} else {
				break
			}
		}

		if len(strings.Trim(strings.Trim(strings.Trim(l, " "), "\n"), "\r")) == 0 {
			continue
		}

		if base == "" {
			extendSection := reExtend.FindStringSubmatch(l)
			if len(extendSection) == 2 {
				base = extendSection[1]
				d["base-template"] = base
			}

			continue
		}

		if block == "" {
			startSection := reBlockStart.FindStringSubmatch(l)
			if len(startSection) == 2 {
				block = startSection[1]
			}

			continue
		}

		endSection := reBlockEnd.FindStringSubmatch(l)
		if len(endSection) == 0 {
			value = value + " " + l
			continue
		} else if len(endSection) == 1 {
			d[block] = value
			block = ""
			endSection = nil
			value = ""
			continue
		}

	}

	if block != "" {
		err := (TplError)("Unclose block: " + block)
		return nil, err
	}

	return d, nil
}
